- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
std: don't leak the thread closure if destroying the thread attributes fails #148026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
| rustbot has assigned @Mark-Simulacrum. Use  | 
| ) -> io::Result<Thread> { | ||
| let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f })); | ||
| let mut native: libc::pthread_t = mem::zeroed(); | ||
| let data = Box::new(ThreadData { name: name.map(Box::from), f }); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this declaration could be moved down in its entirety. I'm not sure why you only did that for the Box::into_raw part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mistakenly assumed that the Box::from implementation could panic, in which case this should do as little other work as possible. That's not the case, but I'll still leave it here in case something changes in the future.
| let mut attr = DropGuard::new(&mut attr, |attr| { | ||
| assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0) | ||
| }); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps you could add a comment here to explain the relevant considerations of using DropGuard here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I notice is that before, libc::pthread_attr_destroy(attr.as_mut_ptr()) is not called for #[cfg(any(target_os = "espidf", target_os = "nuttx"))], but with this change it will/could be. Is that intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is very much intended. The leakage of the current version is a bug. And do you really think that this needs documentation? The DropGuard just makes sure that the attribute structure is destroyed, which is pretty self-explanatory to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The what is clear enough, but the why could use some expanding. If it was completely straightforward, then this bug wouldn't have needed fixing, so I do think explaining why the DropGuard is needed would be good.
| return if ret != 0 { | ||
| // The thread failed to start and as a result p was not consumed. Therefore, it is | ||
| // safe to reconstruct the box so that it gets deallocated. | ||
| return if ret == 0 { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be the last expression of this function, so maybe does not need return?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As there is an item (the thread_start function) after this, the return is required by the parser.
| extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { | ||
| unsafe { | ||
| let data = Box::from_raw(data as *mut ThreadData); | ||
| // Next, set up our stack overflow handler which may get triggered if we run | ||
| // out of stack. | ||
| let _handler = stack_overflow::Handler::new(data.name); | ||
| // Finally, let's run some code. | ||
| (data.f)(); | ||
| } | ||
| ptr::null_mut() | ||
| } | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only used on line 107 I think, so maybe move just above there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd find that very confusing, as it would mix the function item together with the logic of new. In any case, I don't want to do it in this PR.
The comment about double-free is wrong – we can safely drop both the thread attributes and the thread closure. Here, I've used
DropGuardfor the attributes and moved theBox::into_rawto just before thepthread_create.